/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

// Hovmålvej 74/8Hovmålvej 74/8

package EJB_GameLogic;

import EntityBeans.Game;
import EntityBeans.Gamesession;
import EntityBeans.User;
import java.math.BigInteger;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;
import javax.ejb.Stateless;
import javax.faces.convert.IntegerConverter;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 *
 * @author DROWNE
 */
@Stateless
public class GameLogic implements GameLogicRemote {
    
    @PersistenceContext(unitName="HasServerPU")
    EntityManager em;

    @Override
    public int registerUser(String _username, String _password, String _nickName, String _email) {
        String query="select u from User u where u.username=:user";
        Query q=em.createQuery(query);
        q.setParameter("user", _username);
        Iterator itResult=q.getResultList().iterator();
        if(itResult.hasNext()){
            return -1;
        } else {
            User us = new User();
            us.setEmail(_email);
            us.setNickname(_nickName);
            us.setPassword(_password);
            us.setUsername(_username);
            em.persist(us);
            em.flush();
            return 1;
        }
    }

    @Override
    public int loginUser(String _username, String _password) {
        String query="select u from User u where u.username=:user and u.password=:pass";
        Query q=em.createQuery(query);

        q.setParameter("user", _username);
        q.setParameter("pass",_password);

        Iterator itResult=q.getResultList().iterator();
        
        if(itResult.hasNext()){
            return 1;
        }

        return 0;
    }

    @Override
    public int setPosition(int gameSessionID, int latitude, int longitude) {

        String query="select u from Gamesession u where u.idgamesession=:idgamesession";
        Query q=em.createQuery(query);

        q.setParameter("idgamesession", gameSessionID);

        Iterator itResult=q.getResultList().iterator();

        if(itResult.hasNext()){

            // if a game session was found
            // update latitude and longitude

            Gamesession gs = (Gamesession) itResult.next();
            gs.setLatitude(latitude);
            gs.setLongitude(longitude);
            em.persist(gs);
            em.flush();

            return 1;
        }
        
        return -1;
    }

    @Override
    public int createNewgame(String owner, int duration) {

        Date currentTime = new Date();
        // 60'000 milliseconds == 1 minute
        Date endTime =  new Date( currentTime.getTime() + (duration*60000) );

        Game us = new Game();
        us.setOwner(owner);
        us.setTimestampbegin(currentTime);
        us.setTimestampend(endTime);
        
        em.persist(us);
        em.flush();

        // now check if the game was inserted in the database
        // and return the gameId

        String query="select u from Game u where u.owner=:user and u.timestampbegin=:timestampbegin";
        Query q=em.createQuery(query);

        q.setParameter("user", owner);
        q.setParameter("timestampbegin",currentTime);

        if(!q.getResultList().isEmpty()){

            Game temp = (Game) q.getResultList().get(0);

            return temp.getIdgame();

        }

        return -1;

    }

    @Override
    public String getAvailableGames() {

        String query="select u from Game u";
        Query q=em.createQuery(query);
        Iterator itResult=q.getResultList().iterator();

        int sizeFirstArray = q.getResultList().size();

        String gameResult="";
        Date currentTime = new Date();
        Date playTime;
        int i=0;

        while(i<sizeFirstArray){

            // if the game is still open
            // return the ids of the games
            // the game is open if the timestampend
            // is before currentTime

            Game temp = (Game) q.getResultList().get(i++);
            
            playTime = temp.getTimestampend();
            
            // Filter the results and
            // organize in strings to cut them later

            if(currentTime.before(playTime)) {

                gameResult += temp.getIdgame() +"|";
                gameResult += temp.getOwner() +"|";

            }

        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;

    }

    @Override
    public int joinGame(int idgame, String username) {

        // Before joining a new game check if the player has already joined
        String query="SELECT gs FROM Gamesession gs JOIN gs.user AS u JOIN gs.game AS g WHERE u.username=:username AND g.idgame=:idgame";
        
        Query q=em.createQuery(query);

        q.setParameter("idgame", idgame);
        q.setParameter("username", username);
        Iterator itResult=q.getResultList().iterator();
        if(itResult.hasNext()){
            // already joined
            return -1;
        } else {
            
            Game game = getGameFromId(idgame);
            User user = getUserFromUsername(username);

            Gamesession gs = new Gamesession();
            gs.setGame(game);
            gs.setUser(user);
            gs.setBeingcapturedby(0);
            em.persist(gs);
            em.flush();

        }

        // now check if the player joined the game and return the gameSessionID
        q=em.createQuery(query);

        q.setParameter("idgame", idgame);
        q.setParameter("username", username);
        
        Iterator itResult2 = q.getResultList().iterator();

        if(itResult2.hasNext()){
            Gamesession gs = (Gamesession) q.getResultList().get(0);
            return gs.getIdgamesession();
        }

        return -1;
    }

    private Game getGameFromId(int idGame) {

        String query="select u from Game u where u.idgame=:idgame";
        Query q = em.createQuery(query);
        q.setParameter("idgame", idGame);

        return (Game) q.getSingleResult();
        
    }

    private Gamesession getGamesessionFromId(int gamesessionID) {

        String query="select u from Gamesession u where u.idgamesession=:gamesessionID";
        Query q = em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        return (Gamesession) q.getSingleResult();

    }

    private User getUserFromUsername(String username) {

        String query="select u from User u where u.username=:username";
        Query q = em.createQuery(query);
        q.setParameter("username", username);

        return (User) q.getSingleResult();
    }

    @Override
    public String getPlaterList(int idgame) {

        String query="SELECT gs FROM Gamesession gs JOIN gs.game AS g WHERE g.idgame=:idgame";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);

        Iterator itResult=q.getResultList().iterator();
        int sizeFirstArray = q.getResultList().size();

        String gameResult="";
        int i = 0;
        
        while(i<sizeFirstArray) {

            Gamesession temp = (Gamesession) q.getResultList().get(i++);

            // Filter the results and
            // organize in strings to cut them later

            gameResult += temp.getUser().getNickname() +"|";

        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;
    }

    @Override
    public int setCenterPosition(int gameID, int latitude, int longitude) {

        String query="select u from Game u where u.idgame=:idgame";
        Query q=em.createQuery(query);

        q.setParameter("idgame", gameID);

        Iterator itResult=q.getResultList().iterator();

        if(itResult.hasNext()){

            // if a game session was found
            // update latitude and longitude

            Game gs = (Game) itResult.next();
            gs.setCenterlat(latitude);
            gs.setCenterlon(longitude);
            em.persist(gs);
            em.flush();

            return 1;
        }

        return -1;

    }

    @Override
    public String getCenterPosition(int idgame) {

        String query="SELECT g FROM Game g WHERE g.idgame=:idgame";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);

        int sizeArray = q.getResultList().size();

        String gameResult="";

       if(sizeArray==1) {

            Game temp = (Game) q.getResultList().get(0);

            // Filter the results and
            // organize in strings to cut them later

            gameResult = temp.getCenterlat() + "|" + temp.getCenterlon();

        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;

    }

    @Override
    public int getGamePhase(int idgame) {

        String query="SELECT g FROM Game g WHERE g.idgame=:idgame";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);

        int sizeArray = q.getResultList().size();
        int result=-1;
        
        if(sizeArray==1) {
            Game temp = (Game) q.getResultList().get(0);
            result = temp.getGamephase();
        }

        return result;
    }

    @Override
    public int setGamePhase(int idgame, int gamePhase) {

        String query="SELECT g FROM Game g WHERE g.idgame=:idgame";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);

        int sizeArray = q.getResultList().size();
        int result=-2;

        if(sizeArray>0) {
            Game temp = (Game) q.getResultList().get(0);
            temp.setGamephase(gamePhase);
            em.persist(temp);
            em.flush();

            return 1;
        }

        return -2;
    }

    @Override
    public String retrievePlayerPosition(String username, int idgame) {

        String query="SELECT gs FROM Gamesession gs JOIN gs.user AS u JOIN gs.game AS g WHERE u.username=:username AND g.idgame=:idgame";
        Query q=em.createQuery(query);
        q.setParameter("username", username);
        q.setParameter("idgame", idgame);

        int sizeFirstArray = q.getResultList().size();

        String gameResult="";

        if(sizeFirstArray>0) {

            Gamesession temp = (Gamesession) q.getResultList().get(0);

            gameResult  = temp.getLatitude() +"|";
            gameResult += temp.getLongitude();

        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;

    }

    @Override
    public String retrievePlayerPositionByGameSession(int gamesessionID) {

      String query="SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";
        Query q=em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);


        int sizeFirstArray = q.getResultList().size();

        String gameResult="";

        if(sizeFirstArray>0) {

            Gamesession temp = (Gamesession) q.getResultList().get(0);

            gameResult  = temp.getLatitude() +"|";
            gameResult += temp.getLongitude();

        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;

    }

    @Override
    public String retrieveAllPlayerPosition(int idgame) {

        String query="SELECT gs FROM Gamesession gs JOIN gs.game AS g WHERE g.idgame=:idgame";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);

        int sizeFirstArray = q.getResultList().size();

        String gameResult="";
        int i = 0;

        while(i<sizeFirstArray) {

            Gamesession temp = (Gamesession) q.getResultList().get(i++);

            gameResult += temp.getIdgamesession() +"|";
            gameResult += temp.getUser().getUsername() +"|";
            gameResult += temp.getLatitude() +"|";
            gameResult += temp.getLongitude() +"|";


        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;

    }

    @Override
    public int getNumPlayers(int idgame) {

        String query="SELECT gs FROM Gamesession gs JOIN gs.game AS g WHERE g.idgame=:idgame";

        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);

        int sizeFirstArray = q.getResultList().size();

        if(sizeFirstArray>0)
            return sizeFirstArray;
        else
            return -1;

    }

    @Override
    public int assignTeam(int gamesessionID, int teamID) {

        String query="SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";
        Query q=em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        int sizeArray = q.getResultList().size();
  
        if(sizeArray>0) {
            Gamesession temp = (Gamesession) q.getResultList().get(0);
            temp.setTeam(teamID);
            em.persist(temp);
            em.flush();

            return 1;
        }

        return -1;
    }

    @Override
    public int getTeam(int gamesessionID) {

        String query="SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";

        Query q=em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        int sizeFirstArray = q.getResultList().size();

        if(sizeFirstArray>0) {
            Gamesession temp = (Gamesession) q.getResultList().get(0);
            return temp.getTeam();
        } else
            return -1;

    }

    @Override
    public int autoAssignTeams(int idgame) {
       
        // retrieve the player list
        // and set all the players
        // to team 0 (hiders) but
        // the random picks, which
        // are one every 4 players
        
        String query="SELECT gs FROM Gamesession gs JOIN gs.game AS g WHERE g.idgame=:idgame";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);

        int i=0;

        int numPlayers = q.getResultList().size();
        
        int numSeekers = numPlayers / 5;
        if(numSeekers == 0) numSeekers = 1;

        Random generator = new Random();

        if(numSeekers == 1) {
            // only one seeker
            // use the fast method

            int seeker = generator.nextInt(numPlayers);

            while(i<numPlayers) {

                Gamesession temp = (Gamesession) q.getResultList().get(i);

                if(i != seeker)
                    temp.setTeam(0);
                else
                    temp.setTeam(1);

                i++;

                em.persist(temp);
                em.flush();

            }
        } else {

            // more than one seeker

            int[] seekers = new int[numSeekers];
            for(int z=0; z<numSeekers; z++) {
                // generate the needed random
                seekers[z] = generator.nextInt(numPlayers);
            }

            while(i<numPlayers) {

                Gamesession temp = (Gamesession) q.getResultList().get(i);
                temp.setTeam(0);

                i++;

                em.persist(temp);
                em.flush();
            }

            for(int z=0; z<numSeekers; z++) {
                // generate the needed random
                Gamesession temp = (Gamesession) q.getResultList().get( seekers[z] );
                
                temp.setTeam(1);

                em.persist(temp);
                em.flush();
            }

        }

        // return the number of the seekers
        return numSeekers;
    }

    @Override
    public int getPlayerState(int gamesessionID) {

        String query = "SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";

        Query q = em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        int sizeFirstArray = q.getResultList().size();

        if (sizeFirstArray > 0) {
            Gamesession temp = (Gamesession) q.getResultList().get(0);

            return temp.getPlayerstate();

        }

        return -1;

    }

    @Override
    public int getSimplePlayerState(int gamesessionID) {

        String query = "SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";

        Query q = em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        int sizeFirstArray = q.getResultList().size();

        if (sizeFirstArray > 0) {

            Gamesession temp = (Gamesession) q.getResultList().get(0);
            return temp.getPlayerstate();

        }

        return -1;

    }

    @Override
    public int setPlayerState(int gamesessionID, int state) {
        
        String query="SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";

        Query q=em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        int sizeFirstArray = q.getResultList().size();

        if(sizeFirstArray>0) {
            Gamesession temp = (Gamesession) q.getResultList().get(0);
            temp.setPlayerstate(state);
            em.persist(temp);
            em.flush();

            return 1;
            
        }

        return -1;
    }

    @Override
    public int getCaptureState(int gamesessionID) {

        String query = "SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";

        Query q = em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        int sizeFirstArray = q.getResultList().size();

        if (sizeFirstArray > 0) {

            Gamesession temp = (Gamesession) q.getSingleResult();
            int team         = temp.getTeam();
            Game tempGame    = (Game) temp.getGame();
            int gameid       = tempGame.getIdgame();
            
            if (team == 1) {

                // if you are an alien
               // retrieve the position of the humans

                int captureState = temp.getCapturestate();
                if(captureState == 0) {
                    captureState = 10;
                    temp.setCapturestate(captureState);
                    em.persist(temp);
                    em.flush();
                }

                String result = retrieveEnemiesPositions(gameid, gamesessionID, 1);
                
                if (result.equalsIgnoreCase("EMPTY")) {
                    // no more enemies
                    // set the gamePhase to ended
                    setGamePhase(gameid, 4);

                    return captureState;

                } else {

                    String[] enemieslocation = result.split("\\|");

                    Double latVal1 = temp.getLatitude() / 1E6;
                    Double lonVal1 = temp.getLongitude() / 1E6;

                    for(int i=0; i<enemieslocation.length; i+=3) {

                        Double latVal2 = Integer.parseInt(enemieslocation[i]) / 1E6;
                        Double lonVal2 = Integer.parseInt(enemieslocation[i+1]) / 1E6;

                        double distance = calculateDistance(latVal1.toString(), lonVal1.toString(), latVal2.toString(), lonVal2.toString());

                        int humanGameSessionid = Integer.parseInt(enemieslocation[i+2]);
                        Gamesession humanGameSession = getGamesessionFromId(humanGameSessionid);
                        int humanCapturingBy = humanGameSession.getBeingcapturedby();

                        if (distance <= 20) {
                        // if the human is in range, capture him

                            if (humanCapturingBy == 0) {
                                // if the player is not being captured
                                // by other players
                                // change the capturestate for both players
                                setCaptureState(gamesessionID, 11);
                                setCaptureState(humanGameSessionid, 1);
                                // set me as capturing him
                                humanGameSession.setBeingcapturedby(gamesessionID);
                                em.persist(humanGameSession);
                                em.flush();

                                return 11;
                            }

                        } else {

                            // if the players are not in range anymore, restore their capure state
                            if(humanCapturingBy == gamesessionID) {

                                // reset capturing states
                                setCaptureState(gamesessionID, 13);
                                setCaptureState(humanGameSessionid, 3);
                                // set nobody as capturing him
                                humanGameSession.setBeingcapturedby(0);
                                em.persist(humanGameSession);
                                em.flush();

                                return 13;
                            }

                        }

                    }

                    return temp.getCapturestate();

                }
            }

            // human capture state
            return temp.getCapturestate();

        }

        return -1;

    }

        @Override
    public int setCaptureState(int gamesessionID, int state) {

        String query="SELECT gs FROM Gamesession gs WHERE gs.idgamesession=:gamesessionID";

        Query q=em.createQuery(query);
        q.setParameter("gamesessionID", gamesessionID);

        int sizeFirstArray = q.getResultList().size();

        if(sizeFirstArray>0) {
            Gamesession temp = (Gamesession) q.getResultList().get(0);
            temp.setCapturestate(state);
            em.persist(temp);
            em.flush();

            return 1;

        }

        return -1;
    }

    @Override
    public String retrieveTeamMatesPositions(int idgame, int gamesessionID, int team) {

        String query="SELECT gs FROM Gamesession gs JOIN gs.game AS g WHERE g.idgame=:idgame AND gs.team=:team AND gs.idgamesession<>:gamesessionID";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);
        q.setParameter("gamesessionID", gamesessionID);
        q.setParameter("team", team);

        int sizeFirstArray = q.getResultList().size();

        String gameResult="";
        int i=0;

        if(i<sizeFirstArray) {

            Gamesession temp = (Gamesession) q.getResultList().get(i++);

            gameResult += temp.getLatitude()  +"|";
            gameResult += temp.getLongitude() +"|";

        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;

    }

        @Override
    public String retrieveEnemiesPositions(int idgame, int gamesessionID, int team) {

        String query="SELECT gs FROM Gamesession gs JOIN gs.game AS g WHERE g.idgame=:idgame AND gs.team<>:team AND gs.idgamesession<>:gamesessionID";
        Query q=em.createQuery(query);
        q.setParameter("idgame", idgame);
        q.setParameter("gamesessionID", gamesessionID);
        q.setParameter("team", team);

        int sizeFirstArray = q.getResultList().size();

        String gameResult="";
        int i=0;

        if(i<sizeFirstArray) {

            Gamesession temp = (Gamesession) q.getResultList().get(i++);

            gameResult += temp.getLatitude()        +"|";
            gameResult += temp.getLongitude()       +"|";
            gameResult += temp.getIdgamesession()   +"|";

        }

        if(gameResult.equalsIgnoreCase("")) gameResult = "EMPTY";

        return gameResult;

    }

    @Override
    public double calculateDistance(String latVal1, String lonVal1, String latVal2, String lonVal2) {

        double latVal1Double, lonVal1Double, latVal2Double, lonVal2Double;

        latVal1Double = Double.parseDouble(latVal1);
        lonVal1Double = Double.parseDouble(lonVal1);
        latVal2Double = Double.parseDouble(latVal2);
        lonVal2Double = Double.parseDouble(lonVal2);

        double R = 6371000; // metres

        double dlat = Math.toRadians(latVal2Double - latVal1Double);
        double dlong = Math.toRadians(lonVal2Double - lonVal1Double);

        double a = Math.sin(dlat / 2.0) * Math.sin(dlat / 2.0) + Math.cos(Math.toRadians(latVal1Double)) * Math.cos(Math.toRadians(latVal2Double)) * Math.sin(dlong / 2.0) * Math.sin(dlong / 2.0);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        double d = R * c;

        return d;

    }

  
}
